package com.example.gm.service;

import java.nio.charset.Charset;
import java.util.concurrent.TimeUnit;

import org.apache.tomcat.util.codec.binary.Base64;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import com.example.gm.entity.ApiResult;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.gm.sm2.SM2Helper;
import com.gm.sm2.SM2KeyHelper;
import com.gm.sm2.SM2KeyPair;
import com.gm.sm4.SM4Helper;
import com.gm.sm4.SM4KeyHelper;
import com.gm.sm4.SM4ModeAndPaddingEnum;

import io.micrometer.common.util.StringUtils;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class GmCryptoService {
	@Autowired
	RedisTemplate<String, String> redisTemplate;
	
	ObjectMapper mapper = new ObjectMapper();
	
	// sm3使用 C1C3C2 模式，注意与前端对应
	public static final SM2Engine.Mode sm2mode = SM2Engine.Mode.C1C3C2;
	// sm4对齐方式SM4_CBC_PKCS7Padding
	public static final SM4ModeAndPaddingEnum sm4padding = SM4ModeAndPaddingEnum.SM4_CBC_PKCS7Padding;
	// 字符编码统一 UTF-8
	public static final Charset charset = Charset.forName("utf-8");
	// 使用 Redis 缓存秘钥对
	public static final String SM2_REDIS_KEY = "sm2-redis-key";

	@Value("${gm.security.sm4key}")
	String sm4key;
	@Value("${gm.security.sm4iv}")
	String sm4iv;
	
	private SM2KeyPair getSM2KeyPair() throws JsonProcessingException {
		String json = this.redisTemplate.opsForValue().get(SM2_REDIS_KEY);
		System.out.println("Redis SM2KeyPair 缓存："+json);
		SM2KeyPair sm2KeyPair = null;
		if(StringUtils.isNotBlank(json)) {
			sm2KeyPair = mapper.readValue(json, SM2KeyPair.class);
		}
		if(sm2KeyPair==null) {
			// 创建一套新的秘钥对
			sm2KeyPair = SM2KeyHelper.generateKeyPair();
			System.out.println("SM2KeyPair 解析失败，创建新的秘钥对：");
			// 缓存到 redis
			json = mapper.writeValueAsString(sm2KeyPair);
			this.redisTemplate.opsForValue().set(
					SM2_REDIS_KEY, // key
					json, // val
					12, // 12个小时有效期
					TimeUnit.HOURS); // 单位十分钟
		}
		System.out.println("SM2KeyPair ：");			
		System.out.println(mapper.writeValueAsString(sm2KeyPair));			
		return sm2KeyPair;
	}
	
	// 提供给前端的字符串格式公钥
	public String getPublicKey() throws JsonProcessingException {
		SM2KeyPair sm2KeyPair = this.getSM2KeyPair();
		String strX = Hex.toHexString(sm2KeyPair.getPublicKeyX());
		String strY = Hex.toHexString(sm2KeyPair.getPublicKeyY());
		//String strPrivate = Hex.toHexString(sm2KeyPair.getPrivateKey());
		// 为了兼容前端 sm-crypto 库
		// 公钥前面的02或者03表示是压缩公钥，04表示未压缩公钥, 04的时候，可以去掉前面的04
		// 公钥格式： 不压缩标志 04 + hex(X) + hex(Y)
		return "04" + strX + strY;
	}

	// 默认使用 C2C3C2 方式加密，注意与前端对应
	public ApiResult encryptC1C3C2(String source) throws JsonProcessingException {
		if(StringUtils.isBlank(source))return ApiResult.error("参数为空");
		SM2KeyPair sm2KeyPair = this.getSM2KeyPair();
		// 构建加密公钥参数
		ECPublicKeyParameters ecPublicKeyParameters = SM2KeyHelper.buildECPublicKeyParameters(sm2KeyPair);
		
		byte[] input = source.getBytes(charset);
		byte[] encryptData = null;
		try {
			encryptData = SM2Helper.encrypt(
					input, 
					ecPublicKeyParameters, 
					sm2mode);
		} catch (Exception e) {
			// 加密失败
			e.printStackTrace();
			return ApiResult.error(e.getMessage());
		}
		
		//加密结果hex格式
		String message = Hex.toHexString(encryptData);
		return ApiResult.ok(message);
	}

	public ApiResult decryptC1C3C2(String source) throws JsonProcessingException {
		System.out.println("解密密文："+source);
		SM2KeyPair sm2KeyPair = this.getSM2KeyPair();
		// 构建解密私钥参数
		ECPrivateKeyParameters ecPrivateKeyParameters = SM2KeyHelper.buildECPrivateKeyParameters(sm2KeyPair.getPrivateKey());
		
		// 密文是hex格式
		byte[] input = Hex.decode(source);
		byte[] decryptData = null;
		try {
			decryptData = SM2Helper.decrypt(
					input,
					ecPrivateKeyParameters, 
					sm2mode);
		} catch (Exception e) {
			// 解密失败
			e.printStackTrace();
			return ApiResult.error(e.getMessage());
		}
		
		// 恢复字符串格式的明文
		String message = new String(decryptData, charset);
		return ApiResult.ok(message);
	}

	// SM4 对称加密 - 使用配置参数秘钥方式
	public ApiResult encryptSm4(String source) {
		byte[] input = source.getBytes(charset);
		byte[] key = Hex.decode(this.sm4key);
		byte[] iv = Hex.decode(this.sm4iv);
		byte[] encryptRet = null;
		try {
			encryptRet = SM4Helper.encrypt(
					input,//输入
					key,//key
					sm4padding,
					iv);
		} catch (Exception e) {
			e.printStackTrace();
			return ApiResult.error(e.getMessage());
		}
		// 对称加密 - Hex格式的密文
		String message = Hex.toHexString(encryptRet);
		return ApiResult.ok(message);
	}

	public ApiResult decryptSm4(String source) {
		// 密文是hex格式
		byte[] input = Hex.decode(source);
		byte[] key = Hex.decode(this.sm4key);
		byte[] iv = Hex.decode(this.sm4iv);
		byte[] decryptRet = null;
		try {
			decryptRet = SM4Helper.decrypt(
					input, 
					key, 
					sm4padding,
					iv);
		} catch (Exception e) {
			e.printStackTrace();
			return ApiResult.error(e.getMessage());
		}
		
		// 对称解密
		String message = new String(decryptRet, charset);
		return ApiResult.ok(message);
	}

}
